// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/math-random.h"

#include "src/assert-scope.h"
#include "src/base/utils/random-number-generator.h"
#include "src/contexts-inl.h"
#include "src/isolate.h"
#include "src/objects/fixed-array.h"
#include "src/objects/smi.h"

namespace v8 {
namespace internal {

    void MathRandom::InitializeContext(Isolate* isolate,
        Handle<Context> native_context)
    {
        Handle<FixedDoubleArray> cache = Handle<FixedDoubleArray>::cast(isolate->factory()->NewFixedDoubleArray(
            kCacheSize, AllocationType::kOld));
        for (int i = 0; i < kCacheSize; i++)
            cache->set(i, 0);
        native_context->set_math_random_cache(*cache);
        Handle<PodArray<State>> pod = PodArray<State>::New(isolate, 1, AllocationType::kOld);
        native_context->set_math_random_state(*pod);
        ResetContext(*native_context);
    }

    void MathRandom::ResetContext(Context native_context)
    {
        native_context->set_math_random_index(Smi::zero());
        State state = { 0, 0 };
        PodArray<State>::cast(native_context->math_random_state())->set(0, state);
    }

    Address MathRandom::RefillCache(Isolate* isolate, Address raw_native_context)
    {
        Context native_context = Context::cast(Object(raw_native_context));
        DisallowHeapAllocation no_gc;
        PodArray<State> pod = PodArray<State>::cast(native_context->math_random_state());
        State state = pod->get(0);
        // Initialize state if not yet initialized. If a fixed random seed was
        // requested, use it to reset our state the first time a script asks for
        // random numbers in this context. This ensures the script sees a consistent
        // sequence.
        if (state.s0 == 0 && state.s1 == 0) {
            uint64_t seed;
            if (FLAG_random_seed != 0) {
                seed = FLAG_random_seed;
            } else {
                isolate->random_number_generator()->NextBytes(&seed, sizeof(seed));
            }
            state.s0 = base::RandomNumberGenerator::MurmurHash3(seed);
            state.s1 = base::RandomNumberGenerator::MurmurHash3(~seed);
            CHECK(state.s0 != 0 || state.s1 != 0);
        }

        FixedDoubleArray cache = FixedDoubleArray::cast(native_context->math_random_cache());
        // Create random numbers.
        for (int i = 0; i < kCacheSize; i++) {
            // Generate random numbers using xorshift128+.
            base::RandomNumberGenerator::XorShift128(&state.s0, &state.s1);
            cache->set(i, base::RandomNumberGenerator::ToDouble(state.s0));
        }
        pod->set(0, state);

        Smi new_index = Smi::FromInt(kCacheSize);
        native_context->set_math_random_index(new_index);
        return new_index.ptr();
    }

} // namespace internal
} // namespace v8
